<?php
/**
 *
 * @author ninetyseven.
 * @date 2023-03-27 22:53
 */

namespace App\Services;

use App\Enum\EnterpriseCommuteEnum;
use App\Enum\RedisEnum;
use Exception;
use Illuminate\Support\Facades\Log;

class EnterpriseCommuteService extends BaseService
{
    /**
     * 调用企业接口，创建企业租户
     * @param $params
     * @return bool|string
     * @throws \Exception
     */
    public static function createEnterpriseTenant($params)
    {
        // 验证手机号存在性
        self::validMobileExists($params['contactMobile']);

        $url =env('ENTERPRISE_SERVER_HOST').EnterpriseCommuteEnum::ENTERPRISE_SERVER_TENANT_CREATE_URL;

        $accessToken  = self::_getAccessToken();

        $headers = self::_getHeaders(['tenant'=>env('ENTERPRISE_TENANT_ID'),'token'=>$accessToken]);

        try {
            return self::_curl_post($url,json_encode($params,true),$headers);
        }catch (\Exception $e){
            throw new \Exception('CURL 请求异常：'.$e->getMessage());
        }
    }

    /**
     * 获取企业员工的可用积分
     * @param String $phone 手机号
     * @param bool $scoreOnly 是否只返回布尔
     * @return int|mixed
     * @throws Exception
     */
    public static function getUserScore(string $phone, bool $scoreOnly=true)
    {
        if( empty($phone) ) return 0;
        try {
            $redisKey = sprintf(RedisEnum::R_INNER_THIRD_USER_NOT_EXISTS, RedisEnum::R_INNER_THIRD_SOURCE_COMMUTE, $phone);
            $validField = EnterpriseCommuteEnum::CACHE_VALID_FIELD;
            $validData = EnterpriseCommuteEnum::CACHE_VALID_DATA;
            $ttl = RedisEnum::EXPIRE_TIME_DEFAULT;
            $result = parent::_cacheableRemoteData( $redisKey, $ttl, (new EnterpriseCommuteService()), 'getCacheUserScore', $phone, $validData,$validField);

            if( $scoreOnly ) return $result['code'] === EnterpriseCommuteEnum::OK_CODE ? $result['data']['enterpriseAxd'] ?? 0 : 0;
            else return $result;

        }catch (\Exception $e){
            throw new \Exception('CURL 请求异常：'.$e->getMessage());
        }
    }

    /**
     * 缓存调用 方法
     *
     * @param string $phone
     * @return mixed
     * @throws Exception
     */
    public function getCacheUserScore(string $phone){
        $url =env('ENTERPRISE_SERVER_HOST').EnterpriseCommuteEnum::ENTERPRISE_SERVER_FETCH_USERINFO_URL;
        $params = [
            'phone' => $phone
        ];
        $headers = self::_getHeaders(['tenant'=>env('ENTERPRISE_TENANT_ID'),'token'=>self::_getAccessToken()]);

        $result = self::_curl_get($url . '?' . http_build_query($params), $headers);

        if( $result['code'] > 0 ){
            Log::error('获取企业通勤员工用户信息：'.var_export($result,true));
            throw new Exception("获取企业通勤员工信息结果异常。");
        }
        return $result;
    }

    /**
     * 刷新接口专用用户的accessTOKEN
     * @return void
     * @throws \Exception
     */
    public static function refreshAccessToken(){
        $redis = app('redis.connection');
        $serverHost = env('ENTERPRISE_SERVER_HOST');
        $loginUrl = $serverHost. EnterpriseCommuteEnum::ENTERPRISE_SERVER_LOGIN_URL;
        $refreshUrl=$serverHost. EnterpriseCommuteEnum::ENTERPRISE_SERVER_REFRESH_URL;
        $account =  env('ENTERPRISE_SERVER_ACCOUNT');
        $password = env('ENTERPRISE_SERVER_PASSWORD');

        $refreshToken = $redis->get(RedisEnum::R_ENTERPRISE_COMMUTE_REFRESH_TOKEN_KEY);
        $params=[
            'login_url'     =>  $loginUrl,
            'username'       =>  $account,
            'password'      =>  $password,
            'refresh_url'   =>  $refreshUrl,
            'refresh_token' =>  $refreshToken
        ];

        try {

            if (empty($refreshToken)) {
                //  通过登录来获取 access token
                self::_updateTokenByLogin($params);
            } else {
                //  通过fresh token来获取 access token
                self::_updateTokenByRefreshToken($params);
            }
        }catch (\Exception $e){
            Log::error('刷新TOKEN 失败:'.$e->getMessage());
            throw new \Exception('刷新TOKEN 失败:'.$e->getMessage());
        }
    }

    /**
     * 增加企业通勤 积分
     * @param int $tenantId 企业租户ID
     * @param int $axd      要增加的积分
     * @throws Exception
     */
    public static function incrTenantAxd(int $tenantId, int $axd)
    {
        $url =env('ENTERPRISE_SERVER_HOST').EnterpriseCommuteEnum::ENTERPRISE_SERVER_TENANT_INCR_AXD_URL;
        try {
            return self::_operateTenantAxd($url,$tenantId,$axd);
        } catch ( \Exception $e) {
            Log::error('增加企业通勤积分失败:'.$e->getMessage());
            throw new \Exception('增加企业通勤积分失败:'.$e->getMessage());
        }

    }

    /**
     * 扣减 企业通勤 积分
     * @param int $tenantId 企业租户ID
     * @param int $axd      要扣减的积分
     * @throws Exception
     */
    public static function decrTenantAxd(int $tenantId, int $axd)
    {
        $url =env('ENTERPRISE_SERVER_HOST').EnterpriseCommuteEnum::ENTERPRISE_SERVER_TENANT_DECR_AXD_URL;
        try {
            self::_operateTenantAxd($url,$tenantId,$axd);
        } catch ( \Exception $e) {
            Log::error('扣减企业通勤积分失败:'.$e->getMessage());
            throw new \Exception('扣减企业通勤积分失败:'.$e->getMessage());
        }
    }

    /**
     * 消费订单
     * @param $orderInfo
     * @return bool|string
     * @throws Exception
     */
    public static function paySxcxOrder($orderInfo)
    {
        try {
            $memo = sprintf(EnterpriseCommuteEnum::ENTERPRISE_SERVER_CONSUME_ORDER_MEMO,$orderInfo->start_address, $orderInfo->end_address);
            $params = [
                'mobile'     => $orderInfo->user_phone,
                'orderNo'       => $orderInfo->order_no,
                'amount'        => intval($orderInfo->amount),
                'status'        => EnterpriseCommuteEnum::ENTERPRISE_SERVER_ORDER_STATUS_CONSUME,
                'memo'          => $memo
            ];
            return self::_consumeSxcxOrder($params);
        } catch ( \Exception $e) {
            Log::error('消费订单失败:'.$e->getMessage());
            throw new \Exception('消费订单失败:'.$e->getMessage());
        }
    }

    /**
     * 验证手机号是否存在
     *
     * @param $phone
     * @return bool
     * @throws Exception
     */
    public static function validMobileExists($phone): bool
    {
        try{
            $result = self::getUserScore( $phone, false );
            if($result['code'] === 0){
                if( empty($result['data']) ) return true;
                else{
                    throw new Exception("此手机号的企业租户ID：".$result['data']["tenantId"]);
                }
            }else{
                throw new Exception("返回结果异常。");
            }

        }catch (Exception $e){
            throw new Exception("验证手机号存在失败：".$e->getMessage());
        }

    }

    /**
     * @param string $userPhone
     * @param string $orderNo
     * @param int $amount
     * @param string $memo
     * @return mixed
     * @throws Exception
     */
    public static function applyFrozenOrder(string $userPhone, string $orderNo, int $amount, string $memo)
    {
        try {
            $params = [
                'mobile' => $userPhone,
                'orderNo' => $orderNo,
                'amount' => $amount,
                'status' => EnterpriseCommuteEnum::ENTERPRISE_SERVER_ORDER_STATUS_CONSUME,
                'memo' => $memo
            ];
            return self::_consumeSxcxOrder($params);
        }catch ( \Exception $e) {
            Log::error('申请冻结通勤积分失败:'.$e->getMessage());
            throw new \Exception('申请冻结通勤积分失败:'.$e->getMessage());
        }
    }
    public static function cancelFrozenOrder($userPhone, $orderNo, $amount)
    {
        try {
            $params = [
                'mobile'     => $userPhone,
                'orderNo'    => $orderNo,
                'paybackAmount'        => $amount,
            ];
            return self::_paybackSxcxOrder($params);
        }catch ( \Exception $e) {
            Log::error('取消冻结通勤积分失败:'.$e->getMessage());
            throw new \Exception('取消冻结通勤积分失败:'.$e->getMessage());
        }
    }
    public static function cancelSxcxOrder($orderInfo, $paybackAmount)
    {
        try {
            $params = [
                'mobile'     => $orderInfo->user_phone,
                'orderNo'       => $orderInfo->order_no,
                'paybackAmount'        => $paybackAmount,

            ];
            return self::_paybackSxcxOrder($params);
        } catch ( \Exception $e) {
            Log::error('消费订单失败:'.$e->getMessage());
            throw new \Exception('消费订单失败:'.$e->getMessage());
        }
    }
    /**
     * 操作 企业租户的积分
     * @param $url
     * @param $tenantId
     * @param $axd
     * @return bool|string
     * @throws Exception
     */
    private static function _operateTenantAxd($url,$tenantId,$axd){
        try {
            $accessToken = self::_getAccessToken();

            $params = [
                'id' => $tenantId,
                'enterpriseAxd' => $axd
            ];

            $headers = self::_getHeaders(['tenant' => env('ENTERPRISE_TENANT_ID'), 'token' => $accessToken]);

            return self::_curl_post($url, json_encode($params, true), $headers, 'PUT');
        }catch (Exception $e){
            Log::error("操作企业租户的积分失败。".$e->getMessage());
            throw new Exception("操作企业租户的积分失败。");
        }
    }
    private static function _updateTokenByLogin($params)
    {
        $data = [
            'password'=>$params['password'],
            'username'=>$params['username']
        ];

        $headers = self::_getHeaders(['tenant'=>env('ENTERPRISE_TENANT_ID')],['token']);

       $result = self::_curl_post($params['login_url'],json_encode($data,true),$headers);
       if( !empty($result)  && empty($result['code'])){
           self::_saveRedisData($result['data']);
       }
    }

    private static function _updateTokenByRefreshToken($params)
    {
        $data = [
            'refreshToken'=>$params['refresh_token']
        ];

        $headers = self::_getHeaders(['tenant'=>env('ENTERPRISE_TENANT_ID')],['token','json']);

        $result = self::_curl_post($params['refresh_url'],http_build_query($data),$headers);

        if( !empty($result)  && empty($result['code'])){
            self::_saveRedisData($result['data'],false);
        }else{
            self::_updateTokenByLogin($params);
        }
    }
    private static function _saveRedisData($data,$loginData=true){
        $redis = app('redis.connection');
        $accessTokenExpireTime = intval($data['expiresTime']/1000)-time();
        $redis->set(RedisEnum::R_ENTERPRISE_COMMUTE_ACCESS_TOKEN_KEY,$data['accessToken'],'EX',$accessTokenExpireTime); // set access token
        if( $loginData ){
            $refreshTokenExpireTime = $accessTokenExpireTime+3600*8;
            $redis->set(RedisEnum::R_ENTERPRISE_COMMUTE_REFRESH_TOKEN_KEY,$data['refreshToken'],'EX',$refreshTokenExpireTime); // set refresh token
        }

    }
    private static function _getAccessToken()
    {
        $redis = app('redis.connection');
        $accessToken  = $redis->get(RedisEnum::R_ENTERPRISE_COMMUTE_ACCESS_TOKEN_KEY);
        if( empty($accessToken) ){
            throw new \Exception('access token 为空：');
        }
        return $accessToken;
    }

    /**
     * 支付 订单
     * @param $params
     * @return mixed
     * @throws Exception
     */
    private static function _consumeSxcxOrder($params)
    {
        try {
            $url =env('ENTERPRISE_SERVER_HOST').EnterpriseCommuteEnum::ENTERPRISE_SERVER_CONSUME_ORDER_URL;

            $headers = self::_getHeaders(['tenant'=>env('ENTERPRISE_TENANT_ID'),'token'=>self::_getAccessToken()]);

            $result = self::_curl_post($url, json_encode($params, true), $headers);
            if( !empty($result) && empty($result['code']) )
               return $result['data'];
            throw new Exception($result['data'] ?? '支付失败');
        }catch (Exception $e){
            Log::error("调用企业员工通勤的支付接口失败：".$e->getMessage());
            throw new Exception( '支付失败');
        }
    }
    private static function _paybackSxcxOrder( $params )
    {
        $url =env('ENTERPRISE_SERVER_HOST').EnterpriseCommuteEnum::ENTERPRISE_SERVER_CANCEL_ORDER_URL;
        try {
            // 补齐参数
            $params['status']        = EnterpriseCommuteEnum::ENTERPRISE_SERVER_ORDER_STATUS_CANCEL;
            $params['cancelDate']    = time() * 1000;

            $headers = self::_getHeaders(['tenant'=>env('ENTERPRISE_TENANT_ID'),'token'=>self::_getAccessToken()]);

            $result = self::_curl_post($url, json_encode($params, true), $headers,'PUT');
            if( !empty($result) && empty($result['code']) )
                return true;
            return $result['msg'] ?? '取消返款失败';
        }catch (Exception $e){
            Log::error("调用企业员工通勤的退款订单接口失败：".$e->getMessage());
            throw new Exception("调用企业员工通勤的退款订单接口失败。");
        }
    }
    private static function _getHeaders($params,$excepts=[]): array
    {
        $headers = [
          'tenant'   =>  'tenant-id: %d',
          'token'        =>  'Authorization: Bearer %s',
          'machine'     =>  'Inner-machine: ' . gethostname(),
          'agent'       =>  'User-Agent: Server curl',
          'json'     =>  'Content-Type: application/json'
        ];
        if(!empty($excepts))foreach( $excepts as $key)unset($headers[$key]);
        foreach( $headers as $key => $value)if(isset($params[$key])) $headers[$key] = sprintf($headers[$key],$params[$key]);
        return array_values($headers);
    }

    private static function _curl_get($url, $headers, $method='')
    {
        Log::notice($method.'======>>>>>>>'.$url);

        $result = curlGet($url,$headers);
        Log::debug($result);
        return $result;
    }
    private static function _curl_post($url,$params,$headers, $method='POST'){
        Log::notice($method.'======>>>>>>>'.$url);
        Log::debug("params=".var_export($params,true));
        $result = curlPost($url,$params,$headers,$method);
        Log::debug($result);
        return $result;
    }



}
